import json
from typing import Dict, Any, Optional, Union, AsyncGenerator

import httpx
from httpx import Response

from app.core.config import settings
from app.core.exceptions import DifyAPIError, AuthenticationError
from app.core.logger import log_request, log_response, log_error

class DifyClient:
    """
    Dify API 客户端基础类
    
    负责处理与Dify API的所有HTTP交互，包括认证、请求发送和错误处理
    """
    def __init__(self, api_key: Optional[str] = None):
        """
        初始化客户端
        
        Args:
            api_key: Dify API密钥，如果不提供则使用配置中的默认值
        """
        self.base_url = settings.DIFY_API_BASE_URL
        self.api_key = api_key or settings.DIFY_API_KEY
        
        if not self.api_key:
            raise AuthenticationError("API密钥未提供，请在环境变量或初始化时设置")
        
        # 设置标准请求头
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
    
    async def request(
        self, 
        method: str, 
        endpoint: str, 
        data: Optional[Dict[str, Any]] = None, 
        params: Optional[Dict[str, Any]] = None, 
        timeout: Optional[float] = None
    ) -> Dict[str, Any]:
        """
        发送HTTP请求到Dify API
        
        Args:
            method: HTTP方法 (GET, POST, PUT, DELETE等)
            endpoint: API端点路径（不包含基础URL）
            data: 请求体数据
            params: URL查询参数
            timeout: 请求超时时间（秒）
            
        Returns:
            解析后的JSON响应
            
        Raises:
            AuthenticationError: 认证失败
            DifyAPIError: API调用出错
            Exception: 其他异常
        """
        url = f"{self.base_url}{endpoint}"
        timeout = timeout or settings.TIMEOUT
        
        log_request(method, url, data)
        
        try:
            async with httpx.AsyncClient() as client:
                response = await client.request(
                    method=method,
                    url=url,
                    json=data,
                    params=params,
                    headers=self.headers,
                    timeout=timeout
                )
                
                # 处理错误响应
                if response.status_code >= 400:
                    await self._handle_error_response(response)
                
                # 处理成功的响应
                response_data = response.json()
                log_response(response.status_code, response_data, url)
                return response_data
                
        except httpx.TimeoutException as e:
            log_error(e, context=f"请求超时: {method} {url}")
            raise DifyAPIError(
                detail="请求超时，请稍后重试", 
                error_code="timeout_error"
            )
        except httpx.RequestError as e:
            log_error(e, context=f"网络请求错误: {method} {url}")
            raise DifyAPIError(
                detail="网络请求错误，请检查网络连接", 
                error_code="network_error"
            )
        except json.JSONDecodeError as e:
            log_error(e, context=f"JSON解析错误: {method} {url}")
            raise DifyAPIError(
                detail="响应数据解析错误", 
                error_code="parse_error"
            )
        except Exception as e:
            log_error(e, context=f"未知错误: {method} {url}")
            raise
    
    async def request_stream(
        self, 
        method: str, 
        endpoint: str, 
        data: Optional[Dict[str, Any]] = None, 
        params: Optional[Dict[str, Any]] = None, 
        timeout: Optional[float] = None
    ) -> Response:
        """
        发送流式HTTP请求到Dify API
        
        Args:
            method: HTTP方法 (GET, POST, PUT, DELETE等)
            endpoint: API端点路径（不包含基础URL）
            data: 请求体数据
            params: URL查询参数
            timeout: 请求超时时间（秒）
            
        Returns:
            原始Response对象，用于流式处理
            
        Raises:
            AuthenticationError: 认证失败
            DifyAPIError: API调用出错
            Exception: 其他异常
        """
        url = f"{self.base_url}{endpoint}"
        timeout = timeout or settings.TIMEOUT
        
        log_request(method, url, data)
        
        try:
            client = httpx.AsyncClient()
            response = await client.request(
                method=method,
                url=url,
                json=data,
                params=params,
                headers=self.headers,
                timeout=timeout
            )
            
            # 处理错误响应
            if response.status_code >= 400:
                await client.aclose()
                await self._handle_error_response(response)
            
            return response
            
        except Exception as e:
            log_error(e, context=f"流式请求错误: {method} {url}")
            try:
                await client.aclose()
            except:
                pass
            raise
    
    async def _handle_error_response(self, response: Response) -> None:
        """
        处理错误响应
        
        Args:
            response: HTTP响应对象
            
        Raises:
            AuthenticationError: 认证失败 (401)
            DifyAPIError: 其他API错误
        """
        try:
            error_data = response.json()
            error_message = error_data.get("message", "未知错误")
            error_code = error_data.get("code", "unknown_error")
        except Exception:
            error_message = response.text or "未知错误"
            error_code = "unknown_error"
            error_data = {}
        
        log_error(Exception(error_message), context=f"API错误 ({response.status_code})")
        
        if response.status_code == 401:
            raise AuthenticationError(detail=error_message)
        else:
            raise DifyAPIError(
                status_code=response.status_code,
                detail=error_message,
                error_code=error_code,
                error_data=error_data
            )
            
    async def stream_response(
        self, 
        response: Response
    ) -> AsyncGenerator[Dict[str, Any], None]:
        """
        处理流式响应，将SSE事件转换为Python字典
        
        Args:
            response: 流式HTTP响应
            
        Yields:
            解析后的SSE事件数据
        """
        try:
            async for line in response.aiter_lines():
                line = line.strip()
                
                # 跳过空行
                if not line:
                    continue
                
                # 解析SSE格式的行
                if line.startswith("data:"):
                    event_data = line[5:].strip()
                    if event_data:
                        try:
                            yield json.loads(event_data)
                        except json.JSONDecodeError:
                            # 对于非JSON格式的行，以原始文本返回
                            yield {"text": event_data, "raw": True}
        finally:
            # 确保响应被关闭
            await response.aclose()
